/***************************************************************************
 *   File Instrument.cpp		author belot nicolas (karrakis)    *
 *   implement Class Instrument                                            *
 *   an interface for managing instrument                                  *
 ***************************************************************************
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "Instrument.h"
#include <pthread.h>
#include <iostream>
#include <sstream>

namespace libtrckr {

struct amitie{
	Instrument* me;
	int filtre;
	string branche;
};
Instrument::BoucleEffet::BoucleEffet():in(0,0),out(0,0),filtre(-1){

}
Instrument::Instrument():abranches(0,string()),_be(24){
	_filters =deque<Filter*>();
	//_be = DynArray<BoucleEffet>(24);//totallement arbitraire
	
}

Instrument::Instrument(const Instrument &m):_filters(m._filters),_be(m._be){}

Instrument::~Instrument(){
	if(_filters.size()!=0){
		for(int i=0;i<_filters.size();i++){
			delete _filters[i];
		}	
	}
}
int Instrument::addFilter( Filter* f,int parent){
	if(f==NULL) return -1;
	int where = _filters.size();
	std::cout << "taille " << where << std::endl;
	std::cout << "taille be" << _be.size() << std::endl;
	_filters.push_back(f);
	BoucleEffet eff;
	eff.filtre=where;
	eff.in=DynArray<int>(1,parent);
	eff.in[0]=parent;
	eff.out=DynArray<int>(1,-1);
	eff.out[0]=-1;
	//look for the parent...
	if(parent!=-1){
		BoucleEffet& par=_be[parent];
		std::cout << "Parent :" << par.filtre << std::endl;
		std::cout << "Parent out[0] :" << par.out[0] << std::endl;
		if(par.out[0]==-1){
			par.out[0]=where;
		}else{
			par.out[par.out.size()]=where;
		}
	}
	_be[where]=eff;
	std::cout << _be[where].in.size() <<  " : " << eff.in.size() << std::endl;
	return where;
}

int Instrument::addFilter( Filter* f,DynArray<int> parent){
	int where = _filters.size();
	_filters.push_back(f);
	BoucleEffet eff;
	eff.filtre=where;
	eff.in=parent;
	eff.out=DynArray<int>(1,-1);
	//look for the parent...
	for(uint i=0;i<parent.size();i++){
		BoucleEffet& par=_be[parent[i]];
		if(par.out[0]==-1){
			par.out[0]=where;
		}else{
			par.out[par.out.size()]=where;
		}
	}
	_be[where]=eff;
	return where;
}


void Instrument::setFilter( Filter* f, int index){
	_filters[index]=f;
}



void Instrument::removeFilter( int index){
	_filters.erase(_filters.begin()+index);
	BoucleEffet eff=_be[index];
	if(eff.in.size()>1 && eff.out.size()>1){
		//we refuse to do something!
	}else if(eff.out.size()>1){
		for(uint j=0;j< eff.out.size();j++){
			for(uint i=0;i<_be[j].in.size();i++){
				if(_be[j].in[i]==index){//c'est mon fils!
					_be[eff.in[0]].out[0]=_be[j].in[i];
					_be[j].in[i]=_be[eff.in[0]].out[0];
				}
			}
		}
		//on chane le pere de ses fils
	}else if(eff.in.size()>1) {
		for(uint j=0;j< eff.in.size();j++){
			for(uint i=0;i<_be[j].out.size();i++){
				if(_be[j].out[i]==index){//c'est mon fils!
					_be[eff.out[0]].in[0]=_be[j].out[i];
					_be[j].out[i]=_be[eff.out[0]].in[0];
				}
			}
		}
		//on change le fils de ses peres
	}else{
		_be[eff.in[0]].out[0]=eff.out[0];
		_be[eff.out[0]].in=eff.in[0];
		//on change le pere et le fils
	}
}
DynArray<Filter*> Instrument::getFiltersList() const{
	DynArray<Filter*> ret(_filters.size(),NULL);
	for(uint i=0;i<_filters.size();i++){
		ret[i]=_filters[i];
	}
	return ret;
}

Filter* Instrument::getFilter() const{
	return _filters[0];
}

void* ApplyFilter(void* param){
		amitie *fr= (amitie*)param;
		DynArray<int>in;
		DynArray<int>out;
		string branche=fr->branche;
		int next=fr->filtre;//0;
		cout << "Fire! " << branche << endl;
		while(next!=-1){
			in=fr->me->_be[next].in;
			out = fr->me->_be[next].out;
			if(in.size()>1){
				for(short int i=0;i<in.size();i++){
					//si le thread n'est pas fini j'attends...
					if(!fr->me->process[fr->me->abranches[in[i]]].fini){
						pthread_join(fr->me->process[fr->me->abranches[in[i]]].tr,NULL);
					}
					//mix sauvage des 2buffer
					for(uint j=0;j<fr->me->buffers[fr->me->abranches[in[i]]]->size();j++){
						fr->me->buffers[branche][j]+=fr->me->buffers[fr->me->abranches[in[i]]][j];
					}
					//destroy in buffer...
					fr->me->buffers.erase(fr->me->abranches[in[i]]);
				}
			}
			//j'minsere dans ma branche
			fr->me->abranches[fr->me->_be[next].filtre]=branche;
			*(fr->me->buffers[branche])<< *(fr->me->_filters[fr->me->_be[next].filtre]);
			//(fr->me->_filters[fr->me->_be[next].filtre])->Preview(*(fr->me->buffers[branche]));
			std::cout << branche << " : "  << fr->me->_be[next].filtre << std::endl;
			if(out.size()>1){
				int tmp=out.size();
				int cnt=0;
				for(short int i=1;i<tmp;i++){
					cout << cnt << endl;
					ostringstream os;
					os << "branche_"<< out[i];
					string s=os.str();
					fr->me->abranches[out[i]]=s;
					Buffer b(*(fr->me->buffers[branche]));
					fr->me->buffers.insert(pair<string,Buffer*>(s,&b) );
					Instrument::Thread t; t.fini=false;
					amitie sh;
					sh.me=fr->me;
					sh.branche=s;
					sh.filtre=out[i];
					std::cout << "Creation branche : " << s << i <<std::endl;
					pthread_create(&t.tr, NULL, ApplyFilter, &sh);
					fr->me->process.insert(pair<string, Instrument::Thread>(s, t));
					cnt++;
				}
				cout << "plop" << endl;
			}
			next=out[0];
			cout << "next filtre" <<  next << endl;
		}
		fr->me->process[branche].fini=true;
		pthread_exit(NULL);
}

Buffer& Instrument::ApplyFilters(Buffer&b){

	//DynArray<BoucleEffet> be; this struct already exists.
	std::cout << _be.size() << std::endl;
	for(uint i=0;i<_be.size() >0;i++){
		std::cout << i << " => " << _be[i].filtre << std::endl;
		if(_be[i].filtre >=0){
			std::cout <<  "Entrée : " << _be[i].in.size() << "  " ;
			if(_be[i].in.size()==0){
				std::cout << _be[i].in[0] << std::endl;
			}else
				for(uint j=0;j<_be[i].in.size();j++){
					std::cout << _be[i].in[j] << " ";
				}
			std::cout<< std::endl << "filtre : " << _be[i].filtre << std::endl << "Sortie : " << _be[i].out.size() << " ";
	
			if(_be[i].out.size()==0){
				std::cout << _be[i].out[0] << std::endl;
			}else
				for(uint j=0;j<_be[i].out.size();j++){
					std::cout << _be[i].out[j] << " ";
				}		
				std::cout << std::endl;
		}
	}
	process = map<string,Thread>();
	buffers = map<string,Buffer*>();
	abranches = DynArray<string>(1,string());

	string myname="branche";
	amitie fr;
	fr.me=this;
	fr.branche=myname;
	fr.filtre=0;
	buffers.insert(pair<string,Buffer*>(myname,&b));
	abranches[0]=myname;
	Thread first;
	first.fini=false;
	pthread_create(&first.tr, NULL, ApplyFilter, &fr);
	process.insert(pair<string, Thread>(myname,first));
	pthread_join(first.tr,NULL);
	
	//mix return buffe
	map<string,Buffer*>::iterator frst=buffers.begin();
	map<string,Buffer*>::iterator it;
	for(it=buffers.begin();it !=buffers.end();it++){
		for(uint i=0;i<(*frst).second->size();i++){
			(*(*frst).second)[i]+=(*(*it).second)[i];
		}
	}
	b = *(*frst).second;
	buffers.erase(buffers.begin(),buffers.end());
	return b;
}

Buffer& Instrument::naturalEffects(Buffer& b ,Sample& s,DynArray<PlayingEffect*>* eff){
	b.flush();
	if(eff==NULL){
		if(b.size()!=0){
			b << s;
		}
		return b;
	}
	int ret, size,sum;
	ret = size = b.capacity();
	sum=0;
	while(sum < size){
		Buffer b2(size-ret);
		if(b2.size()!=0){
			b2 << s;
		}
		ret=size-ret;
		for(uint i=0;i<eff->size();i++){
			if((*eff)[i]!=NULL)
				(*eff)[i]->apply(b2, &ret);
		}
		//appaending data;
		for(int i=0;i<ret && sum+i<size;i++){
			b[sum+i]=b2[i];
		}
		std::cout << "returned size : " << ret << std::endl;
		sum+=ret;
		if(sum>size){
			std::cout << "Data loss" << std::endl;
		}
		//b+=b2;		
	}
	return b;
}

Buffer& grab(Converter& c, Buffer& b, Sample &s, double ratio){
	std::cout << "ratio : " << ratio << std::endl;
	if(ratio==1. || ratio==.0){
		b << s;
	}else{
		c.ratio(ratio);
		Buffer in( b.capacity() );
		c.outputsize(b.capacity());
		in<<s;
		while(!c.convert(in,b)){
			in<<s;
		}
	}
	return b;
}

}

